using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using FireStarter.Base;
using FireStarter.Base.BusinessObjectMerger;
using Firestarter.DB.Schema.BO;
using Firestarter.DB.Schema.Logic;
using Habanero.Base;
using Habanero.BO;
using Habanero.Faces.Base;
using Habanero.Faces.Win;
using TreeViewCancelEventArgs=Habanero.Faces.Base.TreeViewCancelEventArgs;
using TreeViewController=Habanero.Faces.Base.TreeViewController;
using TreeViewEventArgs=Habanero.Faces.Base.TreeViewEventArgs;

namespace Firestarter.DB.Schema.UI
{
    public class DatabaseInfoChangesControl : UserControlWin, IFormControl
    {
        //private TreeViewWin _treeView;
        private DatabaseInfoTreeViewWin _treeView;
        private readonly IControlFactory _factory;
        private FilteredTreeViewController _treeViewController;
        private IPanel _expandCollapsePanel;
        private ICheckBox _unchangedCheckbox;
        private ICheckBox _addedCheckbox;
        private ICheckBox _removedCheckbox;
        private ICheckBox _upadtedCheckbox;
        private ImageList _imageList;
        private IPanel _treeViewHelperPanel;
        private static bool _cascadingCheck;
        private CheckBoxWin _checkBoxAll;
        private bool _checkBoxAllClickFired;
       // private IMainMenuHabanero _treeViewContextMenu;
        private DatabaseInfoContextMenuController _contextMenuController;

        public DatabaseInfoChangesControl()
        {
            _factory = GlobalUIRegistry.ControlFactory;
            SetupTreeView();
            SetupFilterPanel();
            SetupExpandCollapsePanel();
            SetupTreeHelperPanel();
            var borderLayoutManager = _factory.CreateBorderLayoutManager(this);
            borderLayoutManager.AddControl(FilterPanel, BorderLayoutManager.Position.North);
            borderLayoutManager.AddControl(_treeView, BorderLayoutManager.Position.Centre);
            borderLayoutManager.AddControl(_treeViewHelperPanel, BorderLayoutManager.Position.South);
        }

        private void SetupTreeHelperPanel()
        {
            _treeViewHelperPanel = _factory.CreatePanel();

            var flowLayoutManager = new FlowLayoutManager(_treeViewHelperPanel,_factory);

            var expandButton = ControlUtilities.CreateToolstripButton("Expand", "Expand All", Images._7_expand_default, Images._7_expand_hover, Images._7_expand_clicked, 
                        delegate
                        {
                            ExpandAll();
                        });
            var collapseButton = ControlUtilities.CreateToolstripButton("Collapse", "Collapse to Tables and Views", Images._6_collapse_default, Images._6_collapse_hover, Images._6_collapse_clicked, 
                        delegate { CollapseToTableNodes();
                                   CollapseToViewNodes();
                                 });
            flowLayoutManager.AddControl(expandButton);
            flowLayoutManager.AddControl(collapseButton);
            _checkBoxAll = new CheckBoxWin
                           {
                               ThreeState = true,
                               Text = @"Select / deselect All",
                               Width = 200,
                               CheckState = CheckState.Indeterminate
                           };
            _checkBoxAll.Click += delegate
            {
                 
                if (_checkBoxAll.CheckState==CheckState.Checked)
                {
                    SelectAll();
                }
                else
                {
                    UnselectAll();                  
                }
                _checkBoxAll.ThreeState = false;
                _checkBoxAllClickFired = true;

            };
            flowLayoutManager.AddControl(_checkBoxAll);
            flowLayoutManager.GapSize = 5;
        }

        private void SelectAll()
        {
            ITreeNode treeNode = _treeViewController.TreeView.Nodes[0];
            UpdateTreeViewCheckboxes(treeNode, true);
        }

        private void UnselectAll()
        {
            ITreeNode treeNode = _treeViewController.TreeView.Nodes[0];
            UpdateTreeViewCheckboxes(treeNode, false);
        }

        protected void UpdateTreeViewCheckboxes(ITreeNode treeNode, bool check)
        {
            if (treeNode == null) return;
            treeNode.Checked = check;
            foreach (ITreeNode childTreeNode in treeNode.Nodes)
            {
                childTreeNode.Checked = true;
                UpdateTreeViewCheckboxes(childTreeNode, check);
            }
        }

        private void SetupExpandCollapsePanel()
        {
            _expandCollapsePanel = _factory.CreatePanel();
            
            var flowLayoutManager = new FlowLayoutManager(_expandCollapsePanel, _factory);

            ExpandButton = _factory.CreateButton("Expand");
            ExpandButton.Click += delegate
                                     {
                                         ExpandAll();
                                         
                                     };
            CollapseButton = _factory.CreateButton("Collapse");
            CollapseButton.Click += delegate
                                        {
                                            CollapseToTableNodes();
                                            CollapseToViewNodes();
                                        };
            flowLayoutManager.AddControl(ExpandButton);
            flowLayoutManager.AddControl(CollapseButton);
            _expandCollapsePanel.Height = ExpandButton.Height + 5;         
        }

        public IButton CollapseButton { get; private set; }
        public IButton ExpandButton { get; private set; }

        private void ExpandAll()
        {
            _treeView.ExpandAll();
        }

        private void CollapseToTableNodes()
        {
            CollapseAllChildNodesFor(this.Database.Tables);
        }

        private void CollapseToViewNodes()
        {
            CollapseAllChildNodesFor(this.Database.Views);
        }

        private void CollapseAllChildNodesFor(IBusinessObjectCollection tables)
        {
            foreach (IBusinessObject table in tables)
            {
                var node = GetTreeNode(table);
                node.Collapse();
            }
        }

        private ITreeNode GetTreeNode(IBusinessObject table)
        {
            return _treeViewController.GetBusinessObjectTreeNode(table);
        }


        private void SetupFilterPanel()
        {
            FilterPanel = _factory.CreatePanel();
            var flowLayoutManager = new FlowLayoutManager(FilterPanel, _factory);
            _unchangedCheckbox = CreateAndAddFilterCheckbox(flowLayoutManager,  "Unchanged");
            _addedCheckbox = CreateAndAddFilterCheckbox(flowLayoutManager,  "Added");
            _removedCheckbox = CreateAndAddFilterCheckbox(flowLayoutManager,  "Removed");
            _upadtedCheckbox = CreateAndAddFilterCheckbox(flowLayoutManager,  "Updated");
            FilterPanel.Height = _unchangedCheckbox.Height + 5;
        }

        private ICheckBox CreateAndAddFilterCheckbox(LayoutManager flowLayoutManager,  string text)
        {
            ICheckBox checkbox = _factory.CreateCheckBox(true);
            checkbox.Text = text;
            checkbox.CheckedChanged += (sender, e) => UpdateFilter();
            flowLayoutManager.AddControl(checkbox);
            return checkbox;
        }

        private void UpdateFilter()
        {
            IBusinessObject rootNodeBusinessObject = _treeViewController.RootNodeBusinessObject;
            Dictionary<IBusinessObject, ChangeType> allPendingChangeTypes = ChangeTypeTreeInspector.GetAllPendingChangeTypes(rootNodeBusinessObject);
            _treeViewController.ApplyFilter(delegate(IBusinessObject bo)
            {
                var mergableBo = bo as IMergableBo;
                if (mergableBo == null) return true;
                var pendingChangeTypes = allPendingChangeTypes[mergableBo];
                if (_unchangedCheckbox.Checked && HasChangeType(pendingChangeTypes, ChangeType.None)) return true;
                if (_addedCheckbox.Checked && HasChangeType(pendingChangeTypes, ChangeType.Add)) return true;
                if (_removedCheckbox.Checked && HasChangeType(pendingChangeTypes, ChangeType.Delete)) return true;
                if (_upadtedCheckbox.Checked && HasChangeType(pendingChangeTypes, ChangeType.Update)) return true;
                return false;
            });
        }

        private static bool HasChangeType(ChangeType changeType, ChangeType checkChangeType)
        {
            return ((changeType & checkChangeType) == checkChangeType);
        }


        private void SetupTreeView()
        {
            _treeView = new DatabaseInfoTreeViewWin {CheckBoxes = true, SuppressDoubleClickEvent = true}; // TreeViewWin();
            _imageList = new ImageList();
            _imageList.Images.Add("None", Icons.None);
            _imageList.Images.Add("Add", Icons.Add);
            _imageList.Images.Add("Delete", Icons.Delete);
            _imageList.Images.Add("Change", Icons.Change);
            _imageList.Images.Add("Rename", Icons.Rename);
            _treeView.ImageList = _imageList;
            _treeViewController = new FilteredTreeViewController(_treeView);            

            _treeView.MouseDown += delegate
            {
                // This flag is used to determine if we should change the _checkBoxAll back to ThreeState
                _checkBoxAllClickFired = false;

            };


            _contextMenuController = new DatabaseInfoContextMenuController(_treeView);
            //_contextMenuController.BindContextMenu();

            VisibleChanged += delegate
            {
                if (!Visible) return;
                if (Database == null) return;
                _contextMenuController.Database = Database;
            };

            _treeView.BeforeCheck += delegate(object sender, TreeViewCancelEventArgs e)
            {
                if (e.Node.Tag == null) return;
                if (e.Node.Tag is IMergableBo) return;
                e.Cancel = true;
            };

            _treeView.AfterCheck += delegate(object sender, TreeViewEventArgs e)
            {
                if (_cascadingCheck) return;
                ITreeNode node = e.Node;

                ResetCheckBoxAllToThreeState();

                var mergableBo = node.Tag as IMergableBo;
                
                if (mergableBo != null)
                {

                    var solutionTraverser = new SolutionTraverser();
                    solutionTraverser.SetPendingChangeApproved(mergableBo, node.Checked);
                }
            };

            _treeViewController.SetupNodeWithBusinessObject = delegate(ITreeNode node, IBusinessObject businessObject)
            {
                var mergableBo = businessObject as IMergableBo;
                if (mergableBo == null) return;
                try
                {
                    _cascadingCheck = true;
                    node.Checked = mergableBo.PendingChangeApproved;
                    SetNodeImage(mergableBo.PendingChangeType, node);
                    SetNodeText(node, businessObject);
                }
                finally
                {
                    _cascadingCheck = false;
                }
            };

            _treeViewController.SetupNodeWithRelationship = delegate(ITreeNode node, IRelationship relationship)
            {
                ChangeType pendingChangeType = ChangeType.None;
                if (relationship is IMultipleRelationship)
                {
                    var multipleRelationship = (IMultipleRelationship) relationship;
                    IEnumerable<IMergableBo> mergableBos = multipleRelationship.BusinessObjectCollection.OfType<IMergableBo>().Where(mergableBo => mergableBo.PendingChangeType != ChangeType.None);
                    foreach (IMergableBo mergableBo in mergableBos)
                    {
                        if (pendingChangeType == ChangeType.None)
                        {
                            switch (mergableBo.PendingChangeType)
                            {
                                case ChangeType.Add:
                                    pendingChangeType = ChangeType.Add;
                                    continue;
                                case ChangeType.Delete:
                                    pendingChangeType = ChangeType.Delete;
                                    continue;
                            }
                        }
                        pendingChangeType = ChangeType.Update;
                    }
                }
                else if (relationship is ISingleRelationship)
                {
                    var singleRelationship = (ISingleRelationship) relationship;
                    var mergableBo = singleRelationship.GetRelatedObject() as IMergableBo;
                    if (mergableBo != null)
                    {
                        pendingChangeType = mergableBo.PendingChangeType;
                    }
                }

                try
                {
                    _cascadingCheck = true;
                    node.Checked = false;
                }
                finally
                {
                    _cascadingCheck = false;
                }
                SetNodeImage(pendingChangeType, node);
            };
        }


        private void ResetCheckBoxAllToThreeState()
        {
            if (!_checkBoxAllClickFired)
            {
                if (!_checkBoxAll.ThreeState)
                {
                    _checkBoxAll.ThreeState = true;
                    _checkBoxAll.CheckState = CheckState.Indeterminate;
                }
            }
        }

        private void SetNodeText(ITreeNode node, IBusinessObject businessObject)
        {
            if (DoSetNodeText<DBColumn>(node, businessObject, bo => bo.ColumnName )) return;
            if (DoSetNodeText<DBPrimaryKey>(node, businessObject, bo => "Primary" )) return;
            if (DoSetNodeText<DBPrimaryKeyColumn>(node, businessObject, bo => bo.ColumnName )) return;
            if (DoSetNodeText<DBKey>(node, businessObject, bo => bo.KeyName )) return;
            if (DoSetNodeText<DBKeyColumn>(node, businessObject, bo => bo.ColumnName )) return;
            if (DoSetNodeText<DBForeignKey>(node, businessObject, bo => string.Format("{0} ({1})"
                    , bo.ForeignKeyName, bo.RelatedTableName) )) return;
            if (DoSetNodeText<DBForeignKeyColumn>(node, businessObject, bo => string.Format("{0} -> {1}",
                bo.ParentColumn != null ? bo.ParentColumnName : "(Unspecified)", 
                bo.RelatedColumn != null ? bo.RelatedColumnName : "(Unspecified)" ) )) return;
        }

        private bool DoSetNodeText<TBusinessObject>(ITreeNode node, IBusinessObject businessObject, 
            Func<TBusinessObject, string> getTextFunc)
        {
            if (businessObject is TBusinessObject)
            {
                node.Text = getTextFunc((TBusinessObject)businessObject);
                return true;
            }
            return false;
        }

        public DBDatabase Database
        {
            get { return (DBDatabase)_treeViewController.RootNodeBusinessObject; }
            set
            {
                _treeViewController.LoadTreeView(value, 7);
                _treeView.Sort();
            }
        }

        public TreeViewController TreeViewController
        {
            get { return _treeViewController; }
        }

        public IPanel FilterPanel { get; private set; }

        private void SetNodeImage(ChangeType pendingChangeType, ITreeNode node)
        {
            string imageName;
            switch (pendingChangeType)
            {
                case ChangeType.None:
                    imageName = "None";
                    try
                    {
                        _cascadingCheck = true;
                        node.Checked = false;
                    }
                    finally
                    {
                        _cascadingCheck = false;
                    }
                    break;
                case ChangeType.Add:
                    imageName = "Add";
                    break;
                case ChangeType.Delete:
                    imageName = "Delete";
                    break;
                case ChangeType.Update:
                    imageName = "Change";
                    break;
                case ChangeType.Rename:
                    imageName = "Rename";
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            node.SelectedImageIndex = GetImageIndex(imageName);
            node.ImageIndex = GetImageIndex(imageName);
            //TODO Mark 20 Mar 2009: Surely the logic from GetImageIndex below can be used to create implementations for the missing VWG methods commented out below.
            //node.SelectedImageKey = imageName;
            //node.ImageKey = imageName;
        }

        private int GetImageIndex(string imageName)
        {
            return _imageList.Images.IndexOfKey(imageName);
        }

        public void SetForm(IFormHabanero form)
        {
        }
    }
}
